package ae.pcfc.etks.online.fnp.service.impl;

import java.net.ConnectException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.xml.bind.JAXBElement;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.lang.ArrayUtils;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.client.SoapFaultClientException;

import ae.pcfc.etks.online.fnp.exception.FNPException;
import ae.pcfc.etks.online.fnp.service.FNPService;
import ae.pcfc.etks.online.fnp.service.types.CancelPaymentAdviceRequest;
import ae.pcfc.etks.online.fnp.service.types.CancelPaymentAdviceResponse;
import ae.pcfc.etks.online.fnp.service.types.ClientType;
import ae.pcfc.etks.online.fnp.service.types.GeneratePaymentAdviceRequest;
import ae.pcfc.etks.online.fnp.service.types.GeneratePaymentAdviceResponse;
import ae.pcfc.etks.online.fnp.service.types.GetClientPrepaidAccountReportRequest;
import ae.pcfc.etks.online.fnp.service.types.GetClientPrepaidAccountReportResponse;
import ae.pcfc.etks.online.fnp.service.types.GetServiceCatalogueRequest;
import ae.pcfc.etks.online.fnp.service.types.GetServiceCatalogueResponse;
import ae.pcfc.etks.online.fnp.service.types.GetServiceCatalogueResponse.Service;
import ae.pcfc.etks.online.fnp.service.types.GetServiceParametersRequest;
import ae.pcfc.etks.online.fnp.service.types.GetServiceParametersResponse;
import ae.pcfc.etks.online.fnp.service.types.ObjectFactory;
import ae.pcfc.etks.online.fnp.service.types.PaymentAdviceStatusReportRequest;
import ae.pcfc.etks.online.fnp.service.types.PaymentAdviceStatusReportResponse;
import ae.pcfc.etks.online.fnp.service.types.PreviewPaymentAdviceRequest;
import ae.pcfc.etks.online.fnp.service.types.PreviewPaymentAdviceResponse;
import ae.pcfc.etks.online.fnp.service.types.QueryPaymentAdviceRequest;
import ae.pcfc.etks.online.fnp.service.types.QueryPaymentAdviceResponse;
import ae.pcfc.etks.online.fnp.service.types.ServiceParameterType;
import ae.pcfc.etks.online.fnp.service.types.ServicePaymentRequestType;
import ae.pcfc.etks.online.fnp.service.vo.ExtraParametersVO;

public class FNPServiceImpl implements FNPService {

	private static Logger log = LoggerFactory
	.getLogger(FNPServiceImpl.class);

	protected WebServiceTemplate wsTemplate;
	protected Map<String, String> webServicesEndPointsMap;
	// protected Map<String, String> generalProp;
	// protected String classURIValue;
	// protected String serviceName;
	// protected static String SOURCE_ID_KEY = "SourceId";
	// protected static String CATEGORY_KEY = "Category";
	protected static String URI_KEY = "FNPService";

	/**
	 * @param generalProp
	 *            the generalProp to set
	 */
	/*
	 * @Resource(name = "generalProp") public void setGeneralProp(Map<String,
	 * String> generalProp) { this.generalProp = generalProp; }
	 */

	/**
	 * @param wsTemplate
	 *            the wsTemplate to set
	 */
	@Resource(name = "fnpWsTemplate")
	public void setWsTemplate(WebServiceTemplate wsTemplate) {
		this.wsTemplate = wsTemplate;
	}

	/**
	 * @param webServicesEndPointsMap
	 *            the webServicesEndPointsMap to set
	 */
	@Resource(name = "fnpWebServicesEndPointsMap")
	public void setWebServicesEndPointsMap(
			Map<String, String> webServicesEndPointsMap) {
		this.webServicesEndPointsMap = webServicesEndPointsMap;
	}

	public List<ServiceParameterType> getServiceParameters(
			List<String> serviceIds, String sourceID) throws FNPException,
			ConnectException {

		try {
			// 1- prepare request object
			GetServiceParametersRequest serviceParamReq = new GetServiceParametersRequest();
			serviceParamReq.setSource(sourceID);
			serviceParamReq.getServiceIdList().addAll(serviceIds);

			// 2- call fnp webService
			GetServiceParametersResponse serviceParamResp = (GetServiceParametersResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY),
							serviceParamReq);

			// 3- return results
			List<ServiceParameterType> params = new ArrayList<ServiceParameterType>();
			Iterator<GetServiceParametersResponse.Service> it = serviceParamResp
					.getService().iterator();
			while (it.hasNext()) {
				GetServiceParametersResponse.Service serv = it.next();
				params.addAll(serv.getServiceParameter());
			}
			return params;
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}

	public List<GetServiceParametersResponse.Service> getServiceInfoAndParameters(
			List<String> serviceIds, String sourceID) throws FNPException,
			ConnectException {
		try {
			// 1- prepare request object
			GetServiceParametersRequest serviceParamReq = new GetServiceParametersRequest();
			serviceParamReq.setSource(sourceID);
			serviceParamReq.getServiceIdList().addAll(serviceIds);

			// 2- call fnp webService
			GetServiceParametersResponse serviceParamResp = (GetServiceParametersResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY),
							serviceParamReq);

			return serviceParamResp.getService();
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}

	public Double previewPaymentAdvice(ClientType clientType, Date dateTime,
			String remarks, List<ServicePaymentRequestType> services,
			String sourceID) throws FNPException, ConnectException {

		try {
			PreviewPaymentAdviceRequest previewPayReq = new PreviewPaymentAdviceRequest();
			previewPayReq.setClient(clientType);

			if (null != dateTime) {

				Calendar cal = Calendar.getInstance();
				cal.setTime(dateTime);
				ObjectFactory factory = new ObjectFactory();
				previewPayReq.setExpiryDate(factory
						.createPaymentAdviceRequestTypeExpiryDate(cal));
			}
			if (null != remarks) {
				previewPayReq.setRemarks(remarks);
			} else {
				previewPayReq.setRemarks("");
			}
			previewPayReq.setSource(sourceID);

			previewPayReq.getServiceList().addAll(services);

			PreviewPaymentAdviceResponse previewPayResp = (PreviewPaymentAdviceResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY), previewPayReq);

			return previewPayResp.getPaymentAdviceAmount();
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}

	public GeneratePaymentAdviceResponse generatePaymentAdvice(
			ClientType clientType, Date dateTime, String remarks,
			String transRemarks, ExtraParametersVO extraParamVo,
			List<ServicePaymentRequestType> services, String sourceID)
			throws FNPException, ConnectException {

		try {
			GeneratePaymentAdviceRequest genPayReq = new GeneratePaymentAdviceRequest();
			genPayReq.setClient(clientType);
			if (null != dateTime) {
				Calendar cal = Calendar.getInstance();
				cal.setTime(dateTime);

				ObjectFactory factory = new ObjectFactory();
				genPayReq.setExpiryDate(factory
						.createPaymentAdviceRequestTypeExpiryDate(cal));
			}
			if (null != remarks) {
				genPayReq.setRemarks(remarks);
			} else {
				genPayReq.setRemarks("");
			}
			genPayReq.setSource(sourceID);
			genPayReq.setTransactionNumber(extraParamVo.getTransactionNumber());
			genPayReq.setTransactionRemarks(transRemarks);
			genPayReq.setUserName(extraParamVo.getUserName());

			genPayReq.getServiceList().addAll(services);

			GeneratePaymentAdviceResponse genPayResp = (GeneratePaymentAdviceResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY), genPayReq);

			return genPayResp;
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
		// we can return the PDF, and advice amount as well
	}

	public CancelPaymentAdviceResponse cancelPaymentAdvice(
			String transactionNumber, String paymentAdviceNumber,
			String userName, String message, String sourceID)
			throws FNPException, ConnectException {
		try {
			CancelPaymentAdviceRequest cancelPaymentReq = new CancelPaymentAdviceRequest();

			if (message != null) {
				ObjectFactory factory = new ObjectFactory();
				JAXBElement<String> createMessage = factory
						.createCancelPaymentAdviceRequestMessage(message);

				cancelPaymentReq.setMessage(createMessage);
			}
			cancelPaymentReq.setPaymentAdviceNumber(paymentAdviceNumber);
			cancelPaymentReq.setTransactionNumber(transactionNumber);
			cancelPaymentReq.setUserName(userName);

			cancelPaymentReq.setSource(sourceID);

			// 2- call fnp webService
			CancelPaymentAdviceResponse cancelPaymentResp = (CancelPaymentAdviceResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY),
							cancelPaymentReq);

			// 3-return status
			return cancelPaymentResp;
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}

	public String queryPaymentAdviceStatus(String paymentAdviceNumber,
			String sourceID) throws FNPException, ConnectException {

		try {
			// 1- prepare request object
			QueryPaymentAdviceRequest queryPayReq = new QueryPaymentAdviceRequest();
			queryPayReq.setPaymentAdviceNumber(paymentAdviceNumber);
			queryPayReq.setSource(sourceID);

			// 2- call fnp webService
			QueryPaymentAdviceResponse queryPayResp = (QueryPaymentAdviceResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY), queryPayReq);

			// 3-return status
			return queryPayResp.getPaymentAdviceStatus();
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}

	public List<Service> getServiceCatalogue(String sourceID, String category)
			throws FNPException, ConnectException {

		try {
			// 1- prepare Request object
			GetServiceCatalogueRequest srvCatReq = new GetServiceCatalogueRequest();
			srvCatReq.setSource(sourceID);
			srvCatReq.setCategory(category);

			// 2- call fnp webService
			GetServiceCatalogueResponse serCatResp = (GetServiceCatalogueResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY), srvCatReq);

			// 3- return services list
			return serCatResp.getService();
		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}

	public Byte[] getClientPrepaidAccountReport(Date startDate, Date endDate,
			String trakheesID, String sourceID, String exportOption)
			throws FNPException, ConnectException {
		try {
			// 1- prepare Request object
			/*
			 * @XmlElement(required = true, nillable = true)
			 * 
			 * @XmlSchemaType(name = "dateTime") protected XMLGregorianCalendar
			 * startDate;
			 * 
			 * @XmlElement(required = true, nillable = true)
			 * 
			 * @XmlSchemaType(name = "dateTime") protected XMLGregorianCalendar
			 * endDate;
			 * 
			 * @XmlElement(required = true) protected String trakheesId;
			 * 
			 * @XmlElement(required = true) protected String source;
			 * 
			 * @XmlElement(required = true, defaultValue = "PDF") protected
			 * String exportOption;
			 */
			// ObjectFactory factory = new ObjectFactory();
			GetClientPrepaidAccountReportRequest request = new GetClientPrepaidAccountReportRequest();
			// factory.createget
			// JAXBUtil
			//Calendar startDateCal = Calendar.getInstance();
			//Calendar endDateCal = Calendar.getInstance();
			
			GregorianCalendar gregCal = new GregorianCalendar();
			gregCal.setTime(startDate);
			XMLGregorianCalendar startDateCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregCal);			
			gregCal.setTime(endDate);
			XMLGregorianCalendar endDateCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregCal);
			
			//startDateCal.setTime(startDate);
			request.setStartDate(startDateCal);
			
			//endDateCal.setTime(endDate);
			request.setEndDate(endDateCal);
			
			request.setTrakheesId(trakheesID);
			request.setSource(sourceID);
			request.setExportOption(exportOption);

			GetClientPrepaidAccountReportResponse response = (GetClientPrepaidAccountReportResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY), request);

			return ArrayUtils.toObject(response
					.getClientPrepaidAccountReportPDF());

		} catch (SoapFaultClientException soapExp) {
			log.error(soapExp.getMessage());
			throw new FNPException(soapExp.getMessage());
		} catch (Exception exp) {
			log.error(exp.getMessage());
			throw new FNPException(exp);
		}
	}

	public Byte[] paymentAdviceStatusReport(Date startDate, Date endDate,
			String status, String trakheesID, String sourceID, String exportOption)
			throws FNPException, ConnectException {		
		try {
			// 1- prepare Request object
			/*
			 * @XmlElement(required = true, nillable = true)
			 * 
			 * @XmlSchemaType(name = "dateTime") protected XMLGregorianCalendar
			 * startDate;
			 * 
			 * @XmlElement(required = true, nillable = true)
			 * 
			 * @XmlSchemaType(name = "dateTime") protected XMLGregorianCalendar
			 * endDate;
			 * 
			 * @XmlElement(required = true) protected String trakheesId;
			 * 
			 * @XmlElement(required = true) protected String source;
			 * 
			 * @XmlElement(required = true, defaultValue = "PDF") protected
			 * String exportOption;
			 */
			// ObjectFactory factory = new ObjectFactory();
			PaymentAdviceStatusReportRequest request = new PaymentAdviceStatusReportRequest();
			// factory.createget
			// JAXBUtil
			//Calendar startDateCal = Calendar.getInstance();
			//Calendar endDateCal = Calendar.getInstance();
			
			GregorianCalendar gregCal = new GregorianCalendar();
			gregCal.setTime(startDate);
			XMLGregorianCalendar startDateCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregCal);			
			gregCal.setTime(endDate);
			XMLGregorianCalendar endDateCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(gregCal);
		

			//startDateCal.setTime(startDate);
			request.setStartDate(startDateCal);
			//endDateCal.setTime(endDate);
			
			request.setEndDate(endDateCal);
			request.setStatus(status);
			request.setTrakheesId(trakheesID);			
			request.setSource(sourceID);
			request.setExportOption(exportOption);

			PaymentAdviceStatusReportResponse response = (PaymentAdviceStatusReportResponse) wsTemplate
					.marshalSendAndReceive(
							webServicesEndPointsMap.get(URI_KEY), request);

			return ArrayUtils.toObject(response
					.getPaymentAdviceStatusReport());

		} catch (SoapFaultClientException soapExp) {
			throw new FNPException(soapExp);
		} catch (Exception exp) {
			throw new FNPException(exp);
		}
	}
}




